package com.study.springbootsecurity.filter;

import com.study.springbootsecurity.config.properties.JwtConfigProperties;
import com.study.springbootsecurity.constants.JwtTokenConstants;
import com.study.springbootsecurity.entity.LoginUser;
import com.study.springbootsecurity.entity.business.RefreshResponse;
import com.study.springbootsecurity.exception.BusinessException;
import com.study.springbootsecurity.service.AccessTokenBlackService;
import com.study.springbootsecurity.service.impl.TokenService;
import com.study.springbootsecurity.service.impl.UserDetailsServiceImpl;
import com.study.springbootsecurity.util.*;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseCookie;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Optional;

/**
 * 过滤器,在请求过来的时候,解析请求头中的token,再解析token得到用户信息,再存到SecurityContextHolder中
 * 错误处理流程：
 * 当发生异常时，Spring 可能会触发错误处理流程
 * 错误处理可能会创建新的请求或重新执行过滤器
 * @author huangjianyun
 */
@Slf4j
public class JwtTokenOncePerRequestFilter   extends OncePerRequestFilter {
    private final UserDetailsServiceImpl userDetailsServiceImpl;
    private final JwtConfigProperties jwtConfigProperties;
    private final TokenService tokenService;
    private final AccessTokenBlackService blackService;
    public JwtTokenOncePerRequestFilter(UserDetailsServiceImpl userDetailsServiceImpl,  JwtConfigProperties jwtConfigProperties, TokenService tokenService, AccessTokenBlackService blackService) {
        this.userDetailsServiceImpl = userDetailsServiceImpl;
        this.jwtConfigProperties = jwtConfigProperties;
        this.tokenService = tokenService;
        this.blackService = blackService;
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String path = request.getRequestURI();
        //直接放行 请求方式OPTIONS直接忽略
        if(HttpMethod.OPTIONS.toString().equals(request.getMethod())){
            filterChain.doFilter(request,response);
            return;
        }

        if ("/api/user/login".equals(path) || "/api/user/smsLogin".equals(path)) {
            filterChain.doFilter(request, response);
            return;
        }

        // 解析token
        String authHeadToken = request.getHeader(JwtTokenConstants.JWT_TOKEN_HEADER);
        //
        if (authHeadToken != null && authHeadToken.startsWith(JwtTokenConstants.JWT_TOKEN_PREFIX)) {
            String accessToken = authHeadToken.replace(JwtTokenConstants.JWT_TOKEN_PREFIX, "");
            try {
                int i = 0;
                log.info("~~~~~~~~~~~~~~~~第"+(i+1)+"执行JwtTokenOncePerRequestFilter的doFilterInternal方法~~~~~~~~~~");
                // 验证accessToken未过期
                Claims claims = TokenUtil.parseJwt(accessToken, jwtConfigProperties);
                // 解析通过
                if (ObjectUtils.isNotEmpty(claims) && TokenUtil.checkToken(accessToken, jwtConfigProperties)) {
                    // 查询数据库用户信息
                    String userName = claims.getSubject();
                    // 处于黑名单accessToken不要执行 返回
                    // 处于黑名单accessToken 跳过不用继续执行过滤器
                    String deviceFingerprint = DeviceFinerUtil.buildDeviceFingerprint(request);
                    if (blackService.isBlacklisted(deviceFingerprint,userName,accessToken)){
                        ResponseUtil.outJson(response, Result.fail(401,"Token has been black list, please login again"));
                        return;
                    }

                    LoginUser userDetails = (LoginUser) userDetailsServiceImpl.loadUserByUsername(userName);
                    if (ObjectUtils.isNotEmpty(userDetails)) {
                        // 设置Authenticate对象spring security上下文
                        // 创建springSecurity认证令牌对象
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
                        // 设置认证详情（如IP地址、User-Agent等）
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                }
            } catch (ExpiredJwtException ex) {
                // accessToken过期 cookie的刷新令牌不为空
                String refreshToken = Optional.ofNullable(CookieUtil.get(request, "refresh_token")).map(Cookie::getValue).orElse("");
                if (StringUtils.isNotBlank(refreshToken)) {
                    // 刷新令牌处理 获取访问令牌,访问令牌存储请求头
                    RefreshResponse refreshResponse = handleTokenRefresh(request, response, refreshToken);
                    String newAccessToken = refreshResponse.getAccessToken();
                    String newRefreshToken = refreshResponse.getRefreshToken();
                    if (StringUtils.isNotBlank(newAccessToken) && StringUtils.isNotBlank(newRefreshToken)) {
                        // TODO 这里新生成accessToken设置的请求头 更新refreshToken到cookie
                        // 生成刷新令牌添加cookie 设置csrf安全模式
                        // 构建安全的 Cookie
                        ResponseCookie.from("refresh_token",refreshToken)
                                .httpOnly(true)
                                .secure(true)
                                .path("/")
                                .maxAge(jwtConfigProperties.getRefreshExpiration())// 设置 Cookie 有效期为 7 天（单位：秒）
                                .sameSite("Strict")   // 严格限制跨站请求（防御 CSRF 攻击）
                                .build();             // 构建 ResponseCookie 对象
                        CookieUtil.set(response, "refresh_token", newRefreshToken,
                                jwtConfigProperties.getRefreshExpiration(), // 秒
                                "/", // 根路径
                                true,
                                true);
                        response.setHeader(JwtTokenConstants.JWT_TOKEN_HEADER,
                                JwtTokenConstants.JWT_TOKEN_PREFIX + newAccessToken);
                        // 正确做法：将新令牌设置到请求头中，供后续过滤器使用
                        Claims newClaims = TokenUtil.parseJwt(newAccessToken, jwtConfigProperties);
                        String userName = newClaims.getSubject();
                        LoginUser userDetails = (LoginUser) userDetailsServiceImpl.loadUserByUsername(userName);

                        if (ObjectUtils.isNotEmpty(userDetails)) {
                            UsernamePasswordAuthenticationToken authentication =
                                    new UsernamePasswordAuthenticationToken(
                                            userDetails.getUsername(),
                                            userDetails.getPassword(),
                                            userDetails.getAuthorities());
                            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                            SecurityContextHolder.getContext().setAuthentication(authentication);
                        }
                    }
                }
            }
        }
        // 继续过滤器链（无论是否认证，都继续执行后续过滤器）
        filterChain.doFilter(request, response);
    }

    private RefreshResponse handleTokenRefresh(HttpServletRequest request, HttpServletResponse response, String refreshToken) {
        // 1. 获取Refresh Token
        // 2. 调用刷新接口（内部转发）
        //HttpClientUtils.refreshPost("https://127.0.0.1:9097/api/refreshToken",refreshToken,request,response,chain);
        RefreshResponse refreshResponse = new RefreshResponse();
        try {
             refreshResponse = tokenService.refreshToken(request, response, refreshToken, jwtConfigProperties);
            if (refreshResponse.getAccessToken() == null || refreshResponse.getRefreshToken() == null) {
                throw new BusinessException(401, "Token refresh failed");
            }
            return refreshResponse;
        } catch (BusinessException e) {
            // 刷新失败，清除安全上下文并返回错误
            // 记录异常日志
            log.error("Token refresh error: {}", e.getMessage(), e);
            SecurityContextHolder.clearContext();
            // ResponseUtil.outJson(response, Result.fail(401, "Token refresh failed"));
            // 抛出认证异常，由 Security 的 AuthenticationEntryPoint 处理
        } catch (Exception e) {
            log.error("其他异常: {}", e.getMessage(), e);
        }
        return refreshResponse;
    }

}

